/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ctu.labiras.aco;

import java.util.Iterator;
import java.util.List;
import java.util.Random;

/**
 *
 * @author thiago
 */
public class Aco {
    public static double INVALIDO = -1;
    
    private Parametros parametros;
    private Formiga[] formigas;
    private double[] taxaFeromonio;
    private double[] probTransicaoNo;
    private double media;
    private Double melhorAvaliacao;
    private String melhorSolucao;
    private Double piorAvaliacao;
    private String piorSolucao;
            
    public Aco() {
        this.inicializa();
    }   
    
    public Aco(Parametros parametros) {
        this.parametros = parametros;
        this.inicializa();
    }
    
    private int obterNumeroAleatorio(int fim) {
        Random random = new Random();
        return random.nextInt(fim);
    }
    
    private void iniciarFormiga(Formiga formiga) {        
        int numCelulas = parametros.getNumCelulas();
        int celulaAleatoria = obterNumeroAleatorio(numCelulas-1);
        Integer celula = new Integer(celulaAleatoria);
        formiga.setCelula(celula);
        formiga.adicionarNoVisitado(celula);
    }
    
    private void iniciarTaxaFeromonio() {
        int numCelulas = parametros.getNumCelulas();
        this.taxaFeromonio = new double[numCelulas];
        this.probTransicaoNo = new double[numCelulas];
        
        double taxaInicial = parametros.getC();
        for(int i = 0; i < numCelulas; i++) {
            this.taxaFeromonio[i] = taxaInicial;
        }
    }
    
    private void inicializa() {
        //this.parametros = new Parametros();
        int numFormigas = parametros.getNumAnts();
        this.formigas = new Formiga[numFormigas];

        int numCelulas = parametros.getNumCelulas();
        for(int i = 0; i < numFormigas; i++) {
            formigas[i] = new Formiga(numCelulas);
            getFormigas()[i].configurarVizinhos(numCelulas);
        }
        iniciarTaxaFeromonio();
    }
    
    public void construirSolucoes() {
        int numTransicoes = parametros.getNumTransicoes();        
        double alfa = parametros.getAlpha();
        
        for(Formiga formiga: getFormigas()) {            
            if(formiga.getCelula() == null) {
                iniciarFormiga(formiga);
            } else {
                formiga.adicionarCelulaInicial();
            }
            
            List<Integer> nosVizinhos = formiga.getNosVizinhos();
            int j = 0;
            
            for(int z = 1; z < numTransicoes; z++) {                                
                double probTransTotal = .0;
                
                for(Integer indice: nosVizinhos) {
                    if(formiga.comtemNoVisitado(indice) == false) {
                        if(taxaFeromonio[indice] >= 0) {
                            probTransTotal += Math.pow(taxaFeromonio[indice], alfa);
                        }
                    }
                }
             
                for(Integer indice: nosVizinhos) {
                    if(formiga.comtemNoVisitado(indice) == false) {
                        if(taxaFeromonio[indice] >= 0) {
                            probTransicaoNo[indice] = Math.pow(taxaFeromonio[indice], alfa)/probTransTotal;
                        } else {
                            probTransicaoNo[indice] = .0;
                        }
                    }
                }
     
                // Roleta
                double roleta = Math.random();
                double maior = .0;
                double menor = .0;                
                
                for(Integer indice: nosVizinhos) {
                    
                    if(formiga.comtemNoVisitado(indice) == false) {
                        maior += probTransicaoNo[indice];

                        if(roleta >= menor && roleta <= maior) {   
                            formiga.adicionarNoVisitado(indice.intValue());
                            break;
                        } else {
                            menor = maior;
                        }
                    } 
                }
            }           
            formiga.preencheCenario();
        }
    }
    
    // Integrar avaliaÃ§Ã£o com estrategia do testador
    public void avaliarSolucoes() {
        double total = .0;
        
        for(Formiga formiga: getFormigas()) {
            Iterator<Integer> it = formiga.getNosVisitados().iterator();
            int no;
            double soma = 0;
            
            while(it.hasNext()) {
                no = it.next().intValue();
                soma += no;
            }
            formiga.setAvaliacao(soma);
            total += soma;            
        }
        this.media = total/getFormigas().length;
        for(Formiga formiga: getFormigas()) {
            formiga.setAvaliacao(Math.abs(formiga.getAvaliacao()-media));
        }
    }
    
    public void atualizarFeromonio() {
        int tam = taxaFeromonio.length;  
        double q = parametros.getQ();
        double qtdFeromDepositar, avaliacao;
        double persistenciaTrilha = 1 - parametros.getRho();
        
        for(int i = 0; i < tam; i++) {
            taxaFeromonio[i] *= persistenciaTrilha;
            qtdFeromDepositar = .0;
            
            for(Formiga formiga: getFormigas()) {
                if(formiga.comtemNoVisitado(i) == true) {
                    avaliacao = Math.abs(formiga.getAvaliacao());
                    qtdFeromDepositar += q*avaliacao;
                }
            }
            taxaFeromonio[i] += qtdFeromDepositar;
        }
    }
    
    public void checarSolucoes() {
        Double avaliacao;
        int numAnts = parametros.getNumAnts();
        
        //piorAvaliacao = melhorAvaliacao = getFormigas()[0].getAvaliacao();                
        System.out.println("---------------------------------------------------------------------------");
        System.out.println("Avaliacoes");
        //melhorSolucao = getFormigas()[0].exibirCaminho();
        System.out.print("{ ");
        //piorSolucao = getFormigas()[0].exibirCaminho();
        for(Formiga formiga: getFormigas()) {
            avaliacao = formiga.getAvaliacao();
            if(melhorAvaliacao == null || melhorAvaliacao < avaliacao) {
                melhorAvaliacao = avaliacao;
                melhorSolucao = formiga.exibirCaminho();
            } 
            if(piorAvaliacao == null || piorAvaliacao > avaliacao) {
                piorAvaliacao = avaliacao;
                piorSolucao = formiga.exibirCaminho();
            }
            System.out.print(" " + formiga.getAvaliacao());
        }        
        System.out.println("} ");
        
        System.out.println("---------------------------------------------------------------------------");
        System.out.println("Checagem de soluções");
        System.out.println("Melhor Avaliação: " + melhorAvaliacao + " Pior Avaliação: " + piorAvaliacao);
        System.out.println("Melhor Solução: " + melhorSolucao + " Pior Solução: " + piorSolucao);
        System.out.println("---------------------------------------------------------------------------");
    }
    
    public void resetarFormigas() {
        for(Formiga formiga: getFormigas()) {
            formiga.resetarSolucao();
        }
    }
    
    public void executar() {
        int numIteracoes = parametros.getNumIter();
        for(int i = 0 ; i < numIteracoes; i++) {
            construirSolucoes();
            avaliarSolucoes();
            checarSolucoes();
            //evaporarFeromonio();
            atualizarFeromonio();
            resetarFormigas();
            System.out.println("Melhor Avaliação: " + melhorAvaliacao + " Pior Avaliação: " + piorAvaliacao);
            System.out.println("Melhor Solução: " + melhorSolucao + " Pior Solução: " + piorSolucao);
        }
    }

    /**
     * @return the formigas
     */
    public Formiga[] getFormigas() {
        return formigas;
    }
    
    public Double getMelhorAvaliacao() {
        return melhorAvaliacao;
    }
}